﻿using System;
using System.Diagnostics;
using JetBrains.Application;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.CSharp;
using JetBrains.ReSharper.Psi.CSharp.Tree;
using JetBrains.ReSharper.Psi.ExtensionsAPI.Tree;
using JetBrains.ReSharper.Psi.Tree;

namespace Neovolve.Extensibility.ReSharper
{
    /// <summary>
    /// The <see cref="PredefinedTypeUsageNodeDescriptor"/>
    ///   class is used to describe <see cref="IPredefinedTypeUsageNode"/> instances.
    /// </summary>
    internal class PredefinedTypeUsageNodeDescriptor : NodeTypeDescriptorBase
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="PredefinedTypeUsageNodeDescriptor"/> class.
        /// </summary>
        /// <param name="node">
        /// The node.
        /// </param>
        public PredefinedTypeUsageNodeDescriptor(ITreeNode node)
            : base(node)
        {
        }

        /// <summary>
        /// Creates the name of the node for type.
        /// </summary>
        /// <param name="typeName">
        /// Name of the type.
        /// </param>
        /// <returns>
        /// A <see cref="ITreeNode"/> value.
        /// </returns>
        protected override ITreeNode CreateNodeForTypeName(String typeName)
        {
            IPsiModule project = Node.GetPsiModule();
            CSharpElementFactory factory = CSharpElementFactory.GetInstance(project);
            ITypeUsageNode typeUsageNode = factory.CreateTypeUsageNode(typeName);
            IPredefinedTypeUsageNode usageNode = Node as IPredefinedTypeUsageNode;

            if (typeUsageNode == null)
            {
                throw new InvalidOperationException("Failed to create TypeUsageNode for '" + typeName + "'.");
            }

            Debug.Assert(usageNode != null, "Incorrect node type provided");

            if (usageNode.ArrayRanks.Count > 0
                || usageNode.NullableMark != null
                || usageNode.PointerMarks.Count > 0)
            {
                IPredefinedTypeUsageNode usageNodeCopy = usageNode.CopyWithResolve() as IPredefinedTypeUsageNode;

                if (usageNodeCopy == null)
                {
                    throw new InvalidOperationException("Failed to copy IPredefinedTypeUsageNode.");
                }

                if (usageNodeCopy.FirstChild == null)
                {
                    throw new InvalidOperationException("IPredefinedTypeUsageNode does not contain child nodes as expected.");
                }

                using (WriteLockCookie.Create())
                {
                    ModificationUtil.ReplaceChild(usageNodeCopy.FirstChild, typeUsageNode);
                }

                return factory.CreateTypeUsageNode(usageNodeCopy.GetText());
            }

            return typeUsageNode;
        }

        /// <summary>
        /// Gets the name of the node type.
        /// </summary>
        /// <returns>
        /// A <see cref="String"/> instance.
        /// </returns>
        protected override String GetNodeTypeName()
        {
            IPredefinedTypeUsageNode usageNode = Node as IPredefinedTypeUsageNode;

            Debug.Assert(usageNode != null, "Incorrect node type provided");

            return usageNode.ScalarPredefinedTypeName.GetText();
        }

        /// <summary>
        ///   Gets a value indicating whether the node is supported by this descriptor.
        /// </summary>
        /// <value>
        ///   <c>true</c> if the node is supported by this descriptor; otherwise, <c>false</c>.
        /// </value>
        public override Boolean IsSupportedNode
        {
            get
            {
                return Node is IPredefinedTypeUsageNode;
            }
        }
    }
}